﻿using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Analysis;
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using RevitProject.Helper;
using RevitProject.SpaceManager.Core;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; // MessageBox, Application
using Microsoft.Win32; // SaveFileDialog, OpenFileDialog
using RevitProject.SpaceManager.Views; // Dialogs, MainView
using Autodesk.Revit.DB.Mechanical; // Enums, Space
using RevitProject.SpaceManager.ViewModel.SpaceViewModel;
using RevitProject.SpaceManager.ViewModel.DialogViewModelPart;
namespace RevitProject.SpaceManager.ViewModel.HAVCLoadViewModelPart
{
    /// <summary>
    /// 空间类型 (HVACLoadSpaceType) 管理页面的 ViewModel。
    /// </summary>
    public partial class HAVCLoadViewModel : ObservableObject // 使用 CommunityToolkit MVVM
    {
        #region Private Fields
        private readonly Document _doc;
        private readonly UIApplication _uiApp;
        private readonly Autodesk.Revit.ApplicationServices.Application _app; // Revit Application
        private readonly SpaceManagerView _mainView; // 主窗口引用，用于弹出对话框的所有者
        private readonly SpaceManagerVm _mainVm; // 对主 ViewModel 的引用，用于访问其他 VM 和协调

        private ObservableCollection<HVACLoadItemViewModel> _spaceTypeList = new ObservableCollection<HVACLoadItemViewModel>();
        private HVACLoadItemViewModel _selectedSpaceType;
        private bool _isLoading = false;
        private List<HVACLoadSpaceType> _allSpaceTypes = new List<HVACLoadSpaceType>();
        private HashSet<ElementId> _usedSpaceTypeIds = new HashSet<ElementId>();




        #endregion

        #region Column Management Fields & Properties

        // 所有可用列 (属性名 -> 显示名) - 需要根据 HVACLoadItemViewModel 完整定义
        internal readonly Dictionary<string, string> AllAvailableColumns = new Dictionary<string, string>
    {
        // --- 属性名 ---       --- 显示名 (Header) ---
        { nameof(HVACLoadItemViewModel.ParentCategoryName), "父分类" }, // 新增
        { nameof(HVACLoadItemViewModel.Name), "名称" },
        { nameof(HVACLoadItemViewModel.Id), "ID" },
        { nameof(HVACLoadItemViewModel.IsPlenumDisplay), "是否为静压箱" },
        { nameof(HVACLoadItemViewModel.AreaPerPersonDisplay), "人均面积(m²/人)" },
        { nameof(HVACLoadItemViewModel.LightingLoadDensityDisplay), "照明负荷密度(W/m²)" },
        { nameof(HVACLoadItemViewModel.PowerLoadDensityDisplay), "设备负荷密度(W/m²)" },
        { nameof(HVACLoadItemViewModel.SensibleHeatGainPerPersonDisplay), "人员显热(W/人)" },
        { nameof(HVACLoadItemViewModel.LatentHeatGainPerPersonDisplay), "人员潜热(W/人)" },
        { nameof(HVACLoadItemViewModel.HeatingSetpointDisplay), "供暖设定点(°C)" },
        { nameof(HVACLoadItemViewModel.CoolingSetpointDisplay), "制冷设定点(°C)" },
        { nameof(HVACLoadItemViewModel.HumidificationSetpointDisplay), "加湿设定点(%)" },
        { nameof(HVACLoadItemViewModel.DehumidificationSetpointDisplay), "除湿设定点(%)" },
        { nameof(HVACLoadItemViewModel.InfiltrationAirflowPerAreaDisplay), "渗透风量(L/(s·m²))" }, // 参数名称需确认
        { nameof(HVACLoadItemViewModel.OccupancyScheduleDisplay), "占用率明细表" },
        { nameof(HVACLoadItemViewModel.LightingScheduleDisplay), "照明明细表" },
        { nameof(HVACLoadItemViewModel.PowerScheduleDisplay), "电力负荷明细表" },
        { nameof(HVACLoadItemViewModel.OutsideAirPerPersonDisplay), "每人新风量(m³/h)" },
        { nameof(HVACLoadItemViewModel.OutsideAirPerAreaDisplay), "每面积新风量(L/(s·m²))" },
        { nameof(HVACLoadItemViewModel.AirChangesPerHourDisplay), "换气次数(/h)" },
        { nameof(HVACLoadItemViewModel.OutsideAirMethodDisplay), "新风方法" },

    };

        #endregion

        #region Public Properties
        public ObservableCollection<HVACLoadItemViewModel> SpaceTypeList
        {
            get => _spaceTypeList;
            set => SetProperty(ref _spaceTypeList, value); // 允许 set 以便 ApplyFilter 更新
        }

        public HVACLoadItemViewModel SelectedSpaceType
        {
            get => _selectedSpaceType;
            set
            {
                if (SetProperty(ref _selectedSpaceType, value))
                {
                    // 手动通知依赖选中项的命令更新其 CanExecute 状态
                    (EditSpaceTypeCommand as AsyncRelayCommand)?.NotifyCanExecuteChanged(); // 转换为 AsyncRelayCommand
                    (DeleteSpaceTypeCommand as AsyncRelayCommand)?.NotifyCanExecuteChanged(); // 转换为 AsyncRelayCommand
                    Debug.WriteLine($"SelectedSpaceType changed to: {value?.Name ?? "null"}");
                }
            }
        }

        public bool IsLoading
        {
            get => _isLoading;
            set => SetProperty(ref _isLoading, value);
        }
        #endregion

        #region Commands
        public IAsyncRelayCommand LoadDataCommand { get; }
        public IAsyncRelayCommand AddSpaceTypeCommand { get; } // 改为 Async
        public IAsyncRelayCommand EditSpaceTypeCommand { get; } // 改为 Async
        public IAsyncRelayCommand DeleteSpaceTypeCommand { get; } // 改为 Async
        public IAsyncRelayCommand ImportSpaceTypesCommand { get; }
        public IRelayCommand ExportSpaceTypesCommand { get; }
        
        #endregion

        #region Constructor
        //构造函数 
        public HAVCLoadViewModel(SpaceManagerView mainView, Document doc, UIApplication uiApp, SpaceManagerVm mainVm)
        {
            _mainView = mainView ?? throw new ArgumentNullException(nameof(mainView));
            _doc = doc ?? throw new ArgumentNullException(nameof(doc));
            _uiApp = uiApp ?? throw new ArgumentNullException(nameof(uiApp));
            _app = _uiApp.Application;
            _mainVm = mainVm ?? throw new ArgumentNullException(nameof(mainVm)); // 保存主 VM 引用

            _spaceTypeList = new ObservableCollection<HVACLoadItemViewModel>();



            // 初始化命令
            LoadDataCommand = new AsyncRelayCommand(LoadSpaceTypesAsync);
            AddSpaceTypeCommand = new AsyncRelayCommand(ExecuteAddSpaceTypeAsync, CanExecuteAddOrEdit); 
            EditSpaceTypeCommand = new AsyncRelayCommand(ExecuteEditSpaceTypeAsync, CanExecuteEditOrDelete); 
            DeleteSpaceTypeCommand = new AsyncRelayCommand(ExecuteDeleteSpaceTypeAsync, CanExecuteDeleteSelectedSpaceTypes);
            ImportSpaceTypesCommand = new AsyncRelayCommand(ExecuteImportWithAdapter);
            ExportSpaceTypesCommand = new RelayCommand(ExecuteExportSpaceTypes);
           
           
            
        }
        #endregion

        #region Command Methods

        


        // --- 列设置命令执行方法 

        // --- 加载数据 ---
        private async Task LoadSpaceTypesAsync()
        {
            if (IsLoading) return;
            IsLoading = true;
            Debug.WriteLine("HAVCLoadVM: 加载空间类型数据..."); // 更新日志消息
            SelectedSpaceType = null;

            try
            {
                List<HVACLoadSpaceType> loadedTypes = null;
                List<Space> allSpaces = null;

                // 确保 SpaceTabViewModel 的树已准备好 (逻辑不变)
                if (_mainVm?.SpaceVM?.SpaceTypeTreeNodes == null || !_mainVm.SpaceVM.SpaceTypeTreeNodes.Any())
                {
                    Debug.WriteLine("HAVCLoadVM: SpaceVM 树未就绪。尝试先加载 SpaceVM 数据...");
                    if (_mainVm.SpaceVM.LoadSpaceDataCommand.CanExecute(null))
                    {
                        await _mainVm.SpaceVM.LoadSpaceDataCommand.ExecuteAsync(null);
                        await Task.Delay(250);
                    }
                    if (_mainVm.SpaceVM?.SpaceTypeTreeNodes == null || !_mainVm.SpaceVM.SpaceTypeTreeNodes.Any())
                    {
                        Debug.WriteLine("HAVCLoadVM 错误: 加载 SpaceVM 树失败。父名称将不可用。");
                    }
                }

                await Task.Run(() =>
                {
                    loadedTypes = GetAllHvacLoadSpaceTypes(_doc);
                    allSpaces = GetAllSpaces(_doc); // 用于使用检查
                });

                _allSpaceTypes = loadedTypes ?? new List<HVACLoadSpaceType>();

                // 计算使用的类型 ID (逻辑不变)
                _usedSpaceTypeIds.Clear();
                if (allSpaces != null)
                {
                    foreach (var space in allSpaces) { /* ... */ _usedSpaceTypeIds.Add(space.SpaceTypeId); }
                    Debug.WriteLine($"HAVCLoadVM: 找到 {_usedSpaceTypeIds.Count} 个使用的空间类型 ID。");
                }
                var viewModels = new List<HVACLoadItemViewModel>();
                if (_allSpaceTypes != null)
                {
                    var nodeLookup = new Dictionary<ElementId, SpaceTypeTreeNodeViewModel>();
                    if (_mainVm?.SpaceVM?.SpaceTypeTreeNodes != null) // 检查树是否可用
                    {
                        BuildNodeLookupRecursive(_mainVm.SpaceVM.SpaceTypeTreeNodes, nodeLookup);
                    }
                    else
                    {
                        Debug.WriteLine("LoadSpaceTypesAsync WARNING: SpaceVM Tree not available for parent lookup.");
                    }

                    foreach (var type in _allSpaceTypes.OrderBy(t => t.Name))
                    {
                        nodeLookup.TryGetValue(type.Id, out var nodeInTree);
                        string parentName = (nodeInTree?.Parent != null && !nodeInTree.Parent.IsAllSpacesNode)
                                          ? nodeInTree.Parent.DisplayName
                                          : "(根分类/未分类)";
                        viewModels.Add(new HVACLoadItemViewModel(type, parentName));
                    }
                }
                SpaceTypeList = new ObservableCollection<HVACLoadItemViewModel>(viewModels); // 直接更新
                Debug.WriteLine($"HAVCLoadVM: 加载完成。总类型数: {_allSpaceTypes.Count}。");

                
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"HAVCLoadVM 加载数据错误: {ex.ToString()}");
                MessageBox.Show(_mainView, $"加载空间类型时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                _allSpaceTypes.Clear();
                _usedSpaceTypeIds.Clear();
                SpaceTypeList = new ObservableCollection<HVACLoadItemViewModel>(); // 出错时清空
            }
            finally { IsLoading = false; }
        }

        // --- 添加空间类型 ---
        private bool CanExecuteAddOrEdit() => !IsLoading;

        private async Task ExecuteAddSpaceTypeAsync()
        {
            Debug.WriteLine("HAVCLoadVM: Initiating Add Space Type process...");
            IsLoading = true;

            try
            {
                // 1. 准备父节点列表
                IEnumerable<SpaceTypeTreeNodeViewModel> allPossibleParents = GetAllNonLeafNodesFromMainVm();
                Debug.WriteLine($"Found {allPossibleParents?.Count() ?? 0} possible parent nodes for dialog.");

                // 2. 创建对话框 ViewModel
                var dialogVm = new CreateOrEditSpaceTypeViewModel();
                dialogVm.InitializeForCreate(null, allPossibleParents, _doc); // 不预选父节点

                // 3. 创建并显示对话框
                var dialog = new CreateOrEditSpaceTypeDialog(_doc)
                {
                    DataContext = dialogVm,
                    Owner = _mainView
                };

                if (dialog.ShowDialog() == true)
                {
                    Debug.WriteLine("Create dialog returned OK.");
                    // 4. 获取用户输入 DTO
                    var creationData = dialogVm.GetResultData();
                    if (creationData != null)
                    {
                        Debug.WriteLine($"Calling SpaceTypeService.CreateSpaceTypeAsync for '{creationData.Name}'...");
                        // 5. 调用核心服务方法
                        ElementId newId = await SpaceTypeService.CreateSpaceTypeAsync(_uiApp, _mainVm, creationData);

                        if (newId != null && newId != ElementId.InvalidElementId)
                        {
                            Debug.WriteLine($"Successfully created space type with ID: {newId}. Refresh handled by service.");
                            // Service 中的 NotifyViewModelsForUpdate 会处理刷新
                        }
                        else
                        {
                            Debug.WriteLine("Space type creation failed (Service returned invalid ID).");
                            // Service 内部应该已经提示了错误
                        }
                    }
                    else { Debug.WriteLine("Failed to get result data from dialog ViewModel."); }
                }
                else { Debug.WriteLine("User cancelled the create dialog."); }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"!!!!!! EXCEPTION in ExecuteAddSpaceTypeAsync: {ex.ToString()}");
                MessageBox.Show(_mainView, $"执行添加空间类型时发生意外错误: {ex.Message}", "严重错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally { IsLoading = false; }
        }

        // --- 编辑空间类型 ---
        private bool CanExecuteEditOrDelete() => SelectedSpaceType != null && !IsLoading;

        private async Task ExecuteEditSpaceTypeAsync()
        {
            if (!CanExecuteEditOrDelete()) return;

            HVACLoadItemViewModel selectedVM = SelectedSpaceType;
            Debug.WriteLine($"HAVCLoadVM: Initiating Edit for '{selectedVM.Name}' (ID: {selectedVM.Id})...");
            IsLoading = true;

            try
            {
                // 1. 获取要编辑的 Revit 对象
                HVACLoadSpaceType typeToEdit = _doc.GetElement(selectedVM.Id) as HVACLoadSpaceType;
                if (typeToEdit == null || !typeToEdit.IsValidObject)
                {
                    MessageBox.Show(_mainView, "无法找到要编辑的空间类型，可能已被删除或无效。", "错误", MessageBoxButton.OK, MessageBoxImage.Warning);
                    await LoadDataCommand.ExecuteAsync(null);
                    return;
                }

                // 2. 准备父节点列表和当前父节点
                IEnumerable<SpaceTypeTreeNodeViewModel> allPossibleParents = GetAllNonLeafNodesFromMainVm();
                SpaceTypeTreeNodeViewModel currentParentNode = FindParentNodeInTree(selectedVM.Name); // 查找当前父节点
                Debug.WriteLine($"Found {allPossibleParents?.Count() ?? 0} possible parents. Current parent for '{selectedVM.Name}' is '{currentParentNode?.DisplayName ?? "Root/None"}'.");

                // 3. 创建对话框 ViewModel 并初始化
                var dialogVm = new CreateOrEditSpaceTypeViewModel();
                dialogVm.InitializeForEdit(typeToEdit, currentParentNode, allPossibleParents, _doc);

                // 4. 显示对话框
                var dialog = new CreateOrEditSpaceTypeDialog(_doc)
                {
                    DataContext = dialogVm,
                    Owner = _mainView
                };

                if (dialog.ShowDialog() == true)
                {
                    Debug.WriteLine("Edit dialog returned OK.");
                    // 5. 获取用户输入 DTO
                    var updateData = dialogVm.GetResultData(); // 返回 SpaceTypeUserInputData
                    if (updateData != null)
                    {
                        Debug.WriteLine($"Service Call Prep: Name='{updateData.Name}', Parent='{updateData.SelectedParentNode?.DisplayName ?? "null"}', OrigName='{updateData.OriginalName ?? "null"}', OrigParent='{updateData.OriginalParentNode?.DisplayName ?? "null"}'");

                        //    确保 DTO 包含编辑所需信息   
                        updateData.ExistingTypeId = selectedVM.Id; // 确保 ID 被设置
                        updateData.OriginalName = selectedVM.Name; // 确保原始名称被设置
                        updateData.OriginalParentNode = currentParentNode; // 设置原始父节点

                        Debug.WriteLine($"Calling SpaceTypeService.UpdateSpaceTypeAsync for '{updateData.OriginalName}' to '{updateData.Name}'...");
                        // 6. 调用核心服务方法 (假设 Service 能处理 UserInput DTO)
                        bool success = await SpaceTypeService.UpdateSpaceTypeAsync(_uiApp, _mainVm, updateData);

                        if (success) { Debug.WriteLine($"Successfully updated space type '{updateData.Name}'. Refresh handled by service."); }
                        else { Debug.WriteLine("Space type update failed (Service returned false)."); }
                    }
                    else
                    {
                        // 如果 updateData 为 null，记录错误并可能提示用户
                        Debug.WriteLine("ExecuteEditSpaceTypeAsync ERROR: Failed to get result data from dialog ViewModel (updateData is null). Aborting service call.");
                        MessageBox.Show(_mainView, "无法从编辑对话框获取有效数据，操作已中止。", "内部错误", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
                else { Debug.WriteLine("User cancelled the edit dialog."); }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"!!!!!! EXCEPTION in ExecuteEditSpaceTypeAsync: {ex.ToString()}");
                MessageBox.Show(_mainView, $"执行编辑空间类型时发生意外错误: {ex.Message}", "严重错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally { IsLoading = false; }
        }

        //删除命令的 CanExecute 逻辑 
        private bool CanExecuteDeleteSelectedSpaceTypes()
        {
            return !IsLoading && SpaceTypeList != null && SpaceTypeList.Any(item => item.IsSelected);
        }

        // --- 删除空间类型 ---
        private async Task ExecuteDeleteSpaceTypeAsync()
        {
            // 1. 获取选中的、未被使用的类型 ViewModels 和 IDs
            var selectedUnusedItems = SpaceTypeList?
                                      .Where(item => item.IsSelected && !_usedSpaceTypeIds.Contains(item.Id))
                                      .ToList();

            // 2. 检查是否有可删除项
            if (selectedUnusedItems == null || !selectedUnusedItems.Any())
            {
                // 检查是否有选中项但都在使用中
                var selectedInUseCount = SpaceTypeList?.Count(item => item.IsSelected && _usedSpaceTypeIds.Contains(item.Id)) ?? 0;
                if (selectedInUseCount > 0)
                {
                    MessageBox.Show(_mainView, $"选中的 {selectedInUseCount} 个空间类型都在使用中，无法删除。", "无法删除", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
                else
                {
                    MessageBox.Show(_mainView, "请先选择要删除的、且未被使用的空间类型。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
                }
                return;
            }

            // 3. 准备删除列表和路径列表
            List<ElementId> idsToDelete = selectedUnusedItems.Select(vm => vm.Id).ToList();
            List<string> pathsToDelete = new List<string>();
            foreach (var vm in selectedUnusedItems)
            {
                // 查找路径，如果找不到路径，仍然尝试删除 Revit 对象，但在 Service 中处理 JSON 删除失败
                string path = FindNodePathInTree(vm.Name);
                pathsToDelete.Add(path); // 添加路径，可能为 null
                if (path == null)
                {
                    Debug.WriteLine($"ExecuteDeleteSpaceTypeAsync WARNING: Could not find node path for '{vm.Name}'. JSON deletion might fail.");
                }
            }
            string namesToDelete = string.Join(", ", selectedUnusedItems.Select(vm => $"'{vm.Name}'"));
            int countToDelete = selectedUnusedItems.Count;


            // 4. 确认删除
            var confirmResult = MessageBox.Show(_mainView,
                $"确定要删除选中的 {countToDelete} 个未使用的空间类型吗？\n({namesToDelete})\n\n这将同时尝试从分类树中移除对应节点。\n此操作无法撤销！",
                "确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning); // 改为 YesNo

            if (confirmResult != MessageBoxResult.Yes)
            {
                Debug.WriteLine("用户取消了删除操作。");
                return;
            }

            // 5. 执行删除
            IsLoading = true;
            DeleteResult deleteResult = null; // <--- 将 deleteResult 移到 try 外部声明
            try
            {
                // 调用 Service 的批量删除方法
                deleteResult = await SpaceTypeService.DeleteSpaceTypesAsync(_uiApp, _mainVm, idsToDelete, pathsToDelete);

                // ****** 新增：在 Service 调用之后，处理结果之前，移除 ViewModel ******
                if (deleteResult != null && deleteResult.RevitSuccess && deleteResult.DeletedCount > 0)
                {
                    // Service 返回了成功删除的 ID 列表 (假设 Service 返回了这个列表，如果没返回需要修改 Service)
                    // 假设 DeleteResult 包含一个 List<ElementId> SuccessfullyDeletedIds 属性
                    var idsActuallyDeleted = deleteResult.SuccessfullyDeletedIds ?? idsToDelete.Take(deleteResult.DeletedCount); // 如果 Service 没返回具体列表，就假设前几个成功了（不太精确）

                    Debug.WriteLine($"准备从 SpaceTypeList (当前数量: {SpaceTypeList.Count}) 中移除 {idsActuallyDeleted.Count()} 个已删除项的 ViewModel...");
                    // 需要在 UI 线程上操作 ObservableCollection
                    await Application.Current.Dispatcher.InvokeAsync(() =>
                    {
                        // 从后往前遍历删除，避免索引问题
                        for (int i = SpaceTypeList.Count - 1; i >= 0; i--)
                        {
                            // 检查当前 ViewModel 的 Id 是否在成功删除的列表中
                            if (idsActuallyDeleted.Contains(SpaceTypeList[i].Id))
                            {
                                Debug.WriteLine($"  移除 ViewModel: {SpaceTypeList[i].Name}");
                                SpaceTypeList.RemoveAt(i);
                            }
                        }
                    });
                    Debug.WriteLine($"移除 ViewModel 操作完成。SpaceTypeList 剩余数量: {SpaceTypeList.Count}");
                    // ****** 新增结束 ******

                }
                if (deleteResult != null)
                {
                    StringBuilder resultMessage = new StringBuilder();
                    resultMessage.AppendLine($"删除操作完成。");
                    if (deleteResult.DeletedCount > 0) resultMessage.AppendLine($"- 成功删除: {deleteResult.DeletedCount} 个");
                    if (deleteResult.SkippedCount > 0) resultMessage.AppendLine($"- 跳过 (使用中或未找到): {deleteResult.SkippedCount} 个");
                    if (deleteResult.ErrorCount > 0) resultMessage.AppendLine($"- 发生错误: {deleteResult.ErrorCount} 个");
                    if (!deleteResult.JsonSuccess && deleteResult.DeletedCount > 0) resultMessage.AppendLine("- 警告: 分类树配置未能完全更新。"); // <-- JsonSuccess 可能是 false
                    if (!string.IsNullOrEmpty(deleteResult.ErrorMessage)) resultMessage.AppendLine($"\n详细信息: {deleteResult.ErrorMessage}");


                    MessageBoxImage icon;
                    if (deleteResult.OverallSuccess)
                    {
                        icon = MessageBoxImage.Information; // 完全成功
                    }
                    else if (deleteResult.RevitSuccess)
                    {
                        icon = MessageBoxImage.Warning; // Revit 成功，但其他部分失败 (JSON 或 VM 更新)
                    }
                    else
                    {
                        icon = MessageBoxImage.Error; // Revit 操作本身失败
                    }



                    MessageBox.Show(_mainView, resultMessage.ToString(), "删除结果", MessageBoxButton.OK, icon);
                }


            }
            catch (Exception ex)
            {
                Debug.WriteLine($"!!!!!! EXCEPTION in ExecuteDeleteSpaceTypeAsync: {ex.ToString()}");
                MessageBox.Show(_mainView, $"执行删除空间类型时发生意外错误: {ex.Message}", "严重错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                IsLoading = false;
                // 删除后，重新评估 CanExecute 状态
                (DeleteSpaceTypeCommand as AsyncRelayCommand)?.NotifyCanExecuteChanged();
            }
        }


        // --- 导入/导出 
        private void ExecuteExportSpaceTypes()
        {
            Debug.WriteLine("尝试执行导出空间类型 (Directly in ViewModel)...");
            // --- 1. 获取所有空间类型 (直接复用 ViewModel 的 _allSpaceTypes 或者重新获取一次) ---
            // 为了确保最新数据，可以选择重新获取，但如果加载后未变，用 _allSpaceTypes 也可以
            List<HVACLoadSpaceType> typesToExport;
            try
            {
                // 直接使用已加载的数据
                // typesToExport = _allSpaceTypes;
                // 或者总是重新获取
                typesToExport = GetAllHvacLoadSpaceTypes(_doc); // <--- 确保这里能获取到数据！

                if (!typesToExport.Any())
                {
                    MessageBox.Show("项目中没有找到任何 HVAC 空间类型可供导出。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
                    return; // 直接返回
                }
            }
            catch (Exception ex)
            {
                string errorMsg = $"导出时获取空间类型出错: {ex.Message}";
                Debug.WriteLine(errorMsg);
                MessageBox.Show(errorMsg, "导出错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }


            // --- 2. 获取用户指定的保存路径 (使用 WPF SaveFileDialog) ---
            string filePath = string.Empty;
            var saveFileDialog = new SaveFileDialog
            {
                Filter = "CSV 文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
                Title = "选择保存空间类型 CSV 文件的位置",
                FileName = "ExportedSpaceTypes.csv" // 默认文件名
            };

            // 需要在 UI 线程弹出对话框
            if (saveFileDialog.ShowDialog(_mainView) == true) // ShowDialog 返回 nullable bool
            {
                filePath = saveFileDialog.FileName;
                Debug.WriteLine($"用户选择导出路径: {filePath}");
            }
            else
            {
                Debug.WriteLine("用户取消了导出文件选择。");
                return; // 用户取消
            }

            IsLoading = true; // 开始导出，设置加载状态
            try
            {
                // --- 3. 构建 CSV 内容 (参照 SPTest) ---
                StringBuilder csvContent = new StringBuilder();

                // 添加中文表头
                string[] headers = {
                     "名称", "是否为静压箱", "人均面积(m²/人)", "照明负荷密度(W/m²)",
                     "设备负荷密度(W/m²)", "人员显热(W/人)", "人员潜热(W/人)",
                     "供暖设定点(°C)", "制冷设定点(°C)", "加湿设定点(%)", "除湿设定点(%)",
                     "每区域的渗透风量(L/(s·m²))", "占用率明细表", "照明明细表", "电力负荷明细表",
                     "每人新风量(m³/h)", "每面积新风量(L/(s·m²))", "换气次数(/h)", "新风方法"
                 };
                csvContent.AppendLine(string.Join(",", headers));

                // 添加数据行
                foreach (var type in typesToExport)
                {
                    if (type == null || !type.IsValidObject) continue;
                    try
                    {
                        string[] row = new string[headers.Length];
                        int colIndex = 0;
                        row[colIndex++] = EscapeCsvFieldHelper(type.Name);
                        row[colIndex++] = type.IsPlenum ? "是" : "否";
                        row[colIndex++] = FormatValueForCsvHelper(type.AreaPerPerson, UnitTypeId.SquareMeters, "F2", 1e-9);
                        row[colIndex++] = FormatValueForCsvHelper(type.LightingLoadDensity, UnitTypeId.WattsPerSquareMeter, "F2");
                        row[colIndex++] = FormatValueForCsvHelper(type.PowerLoadDensity, UnitTypeId.WattsPerSquareMeter, "F2");
                        row[colIndex++] = FormatValueForCsvHelper(type.SensibleHeatGainPerPerson, UnitTypeId.Watts, "F1");
                        row[colIndex++] = FormatValueForCsvHelper(type.LatentHeatGainPerPerson, UnitTypeId.Watts, "F1");
                        row[colIndex++] = FormatValueForCsvHelper(type.HeatingSetPoint, UnitTypeId.Celsius, "F2");
                        row[colIndex++] = FormatValueForCsvHelper(type.CoolingSetPoint, UnitTypeId.Celsius, "F2");

                        double humidPercent = UnitUtils.ConvertFromInternalUnits(type.HumidificationSetPoint, UnitTypeId.Percentage);
                        row[colIndex++] = humidPercent.ToString("F1", CultureInfo.InvariantCulture) + "%";
                        double dehumidPercent = UnitUtils.ConvertFromInternalUnits(type.DehumidificationSetPoint, UnitTypeId.Percentage);
                        row[colIndex++] = dehumidPercent.ToString("F1", CultureInfo.InvariantCulture) + "%";
                        Parameter infiltrationParam = type.get_Parameter(BuiltInParameter.SPACE_INFILTRATION_PARAM);
                        row[colIndex++] = FormatValueForCsvHelper(infiltrationParam?.AsDouble(), UnitTypeId.LitersPerSecondSquareMeter, "F2");
                        row[colIndex++] = EscapeCsvFieldHelper(GetScheduleNameFromParamHelper(type, BuiltInParameter.SPACE_OCCUPANCY_SCHEDULE_PARAM));
                        row[colIndex++] = EscapeCsvFieldHelper(GetScheduleNameFromParamHelper(type, BuiltInParameter.SPACE_LIGHTING_SCHEDULE_PARAM));
                        row[colIndex++] = EscapeCsvFieldHelper(GetScheduleNameFromParamHelper(type, BuiltInParameter.SPACE_POWER_SCHEDULE_PARAM));
                        row[colIndex++] = FormatValueForCsvHelper(type.OutdoorAirPerPerson, UnitTypeId.CubicMetersPerHour, "F2");
                        row[colIndex++] = FormatValueForCsvHelper(type.OutdoorAirPerArea, UnitTypeId.LitersPerSecondSquareMeter, "F2");
                        row[colIndex++] = FormatValueForCsvHelper(type.AirChangesPerHour, null, "F2");
                        row[colIndex++] = FormatOutdoorAirStandardHelper(type.OutdoorAirFlowStandard);
                        if (colIndex != headers.Length) { Debug.WriteLine($"!!!!!! WARNING: Row length mismatch for type '{type.Name}'"); while (colIndex < headers.Length) { row[colIndex++] = ""; } }
                        csvContent.AppendLine(string.Join(",", row));
                    }
                    catch (Exception ex)
                    {

                        Debug.WriteLine($"处理类型 '{type?.Name}' 时出错: {ex.Message}");
                    }
                }

                // --- 4. 写入文件 ---
                File.WriteAllText(filePath, csvContent.ToString(), new UTF8Encoding(true));

                // --- 5. 显示成功信息 ---
                MessageBox.Show(_mainView, $"空间类型数据已成功导出到:\n{filePath}", "导出成功", MessageBoxButton.OK, MessageBoxImage.Information);

            }
            catch (IOException ioEx) // 文件读写错误
            {
                string errorMsg = $"无法将数据写入文件 '{filePath}'.\n请检查文件是否被其他程序占用或权限是否正确。\n错误: {ioEx.Message}";
                Debug.WriteLine($"ExecuteExportSpaceTypesDirectly IO Error: {errorMsg}");
                MessageBox.Show(_mainView, errorMsg, "文件写入错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            catch (Exception ex) // 其他意外错误
            {
                string errorMsg = $"导出过程中发生意外错误: {ex.Message}";
                Debug.WriteLine($"ExecuteExportSpaceTypesDirectly Error: {errorMsg}");
                MessageBox.Show(_mainView, errorMsg, "导出失败", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                IsLoading = false; // 清除加载状态
            }

        }
        private async Task ExecuteImportWithAdapter()
        {
            Debug.WriteLine("尝试执行导入空间类型 (通过适配器)...");

            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "CSV 文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
                Title = "选择包含空间类型定义的 CSV 文件"
            };

            string csvFilePath = string.Empty;
            if (openFileDialog.ShowDialog(_mainView) == true)
            {
                csvFilePath = openFileDialog.FileName;
            }
            else
            {
                Debug.WriteLine("用户取消导入。");
                return;
            }

            IsLoading = true;
            ImportResult importResult = null; // 用于接收适配器返回的结果



            // 使用 RevitContextHelper 执行适配器逻辑

            //使用TaskCompletionSource 等待结果
            var tcs = new TaskCompletionSource<ImportResult>();
            RevitContextHelper.Execute(uiApp =>
            {
                Debug.WriteLine("--> RevitContextHelper Action: Started.");
                ImportResult localResult = null; // 在 Action 内部使用的结果变量
                try
                {
                    Debug.WriteLine("--> RevitContextHelper Action: Calling ImportCommandAdapter.ImportFromCsv...");
                    localResult = ImportCommandAdapter.ImportFromCsv(_doc, uiApp, csvFilePath); // 调用核心逻辑
                    Debug.WriteLine($"--> RevitContextHelper Action: Import Result - Success: {localResult?.Success}, Created: {localResult?.CreatedCount}, Skipped: {localResult?.SkippedCount}, Errors: {localResult?.ErrorCount}");

                    // *** 将结果设置给 TaskCompletionSource ***
                    tcs.TrySetResult(localResult ?? new ImportResult()); // 如果返回 null，也设置一个默认结果
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"!!!!!! ERROR inside RevitContextHelper Action: {ex.ToString()}");

                    tcs.TrySetException(ex);
                }
                Debug.WriteLine("--> RevitContextHelper Action: Finished.");
            });

            Debug.WriteLine("--> ExecuteImportWithAdapter: Waiting for Revit action to complete via TaskCompletionSource...");

            // 等待 Revit 操作完成并获取结果
            try
            {
                // 使用 await 等待 Task 完成，并获取结果
                importResult = await tcs.Task;
                Debug.WriteLine("--> ExecuteImportWithAdapter: Revit action completed. importResult is populated.");
            }
            catch (Exception ex) // 捕获在 Revit Action 中通过 tcs.TrySetException 设置的异常
            {
                Debug.WriteLine($"--> ExecuteImportWithAdapter: Caught exception from Revit action: {ex.ToString()}");
                importResult = new ImportResult { ErrorCount = 1 }; // 标记为失败
                importResult.Log.AppendLine($"执行导入时发生意外错误: {ex.Message}");
            }

            IsLoading = false;

            // --- 处理并显示结果 ---
            if (importResult != null)
            {
                string summary = $"导入完成。\n\n成功创建: {importResult.CreatedCount}\n跳过 (名称已存在): {importResult.SkippedCount}\n失败: {importResult.ErrorCount}\n";
                string fullMessage = summary;
                if (importResult.Log.Length > 0)
                {
                    // 如果日志过长，只显示部分或提示用户查看日志文件
                    if (importResult.Log.Length > 1000)
                    {
                        fullMessage += "\n详细日志过长，请查看调试输出或日志文件。";
                        Debug.WriteLine("--- Import Log ---");
                        Debug.WriteLine(importResult.Log.ToString());
                        Debug.WriteLine("--- End Import Log ---");
                    }
                    else
                    {
                        fullMessage += "\n详细日志:\n" + importResult.Log.ToString();
                    }
                }

                MessageBoxImage icon = importResult.Success ? MessageBoxImage.Information : MessageBoxImage.Warning;
                if (importResult.ErrorCount > 0 && importResult.CreatedCount == 0 && importResult.SkippedCount == 0) icon = MessageBoxImage.Error; // 如果全是错误
                MessageBox.Show(_mainView, fullMessage, "导入结果", MessageBoxButton.OK, icon);

                if (importResult.CreatedCount > 0)
                {
                    // 刷新列表
                    _mainView.Dispatcher.InvokeAsync(() => LoadDataCommand.Execute(null));
                }
            }
            else
            {
                // 这种情况理论上不应发生，除非 RevitContextHelper 执行失败
                MessageBox.Show(_mainView, "导入操作未能完成或返回结果。", "导入错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Debug.WriteLine("--> ExecuteImportWithAdapter: Finished.");
        }
        #endregion

        #region Helper Methods

        // --- 数据加载辅助 ---



        private List<HVACLoadSpaceType> GetAllHvacLoadSpaceTypes(Document doc)
        {
            try { return new FilteredElementCollector(doc).OfClass(typeof(HVACLoadSpaceType)).Cast<HVACLoadSpaceType>().OrderBy(st => st.Name).ToList(); }
            catch (Exception ex) { Debug.WriteLine($"获取 HVACLoadSpaceType 时出错: {ex.Message}"); return new List<HVACLoadSpaceType>(); }
        }
        private List<Space> GetAllSpaces(Document doc)
        {
            try { return new FilteredElementCollector(doc).OfClass(typeof(SpatialElement)).WhereElementIsNotElementType().OfType<Space>().ToList(); }
            catch (Exception ex) { Debug.WriteLine($"获取所有 Space 时出错: {ex.Message}"); return null; }
        }

        // --- 树节点辅助 ---
        private IEnumerable<SpaceTypeTreeNodeViewModel> GetAllNonLeafNodesFromMainVm()
        {
            List<SpaceTypeTreeNodeViewModel> nonLeafNodes = new List<SpaceTypeTreeNodeViewModel>();
            if (_mainVm?.SpaceVM?.SpaceTypeTreeNodes == null) return nonLeafNodes;

            Action<ObservableCollection<SpaceTypeTreeNodeViewModel>> findNonLeafs = null;
            findNonLeafs = (currentNodes) =>
            {
                if (currentNodes == null) return;
                foreach (var node in currentNodes)
                {
                    if (!node.IsAllSpacesNode && !node.IsUnassignedTypeNode && !node.IsLeafNode) nonLeafNodes.Add(node);
                    if (node.Children != null) findNonLeafs(node.Children);
                }
            };
            // 从根节点的子节点开始查找
            if (_mainVm.SpaceVM.SpaceTypeTreeNodes.Count > 0 && _mainVm.SpaceVM.SpaceTypeTreeNodes[0].IsAllSpacesNode)
                findNonLeafs(_mainVm.SpaceVM.SpaceTypeTreeNodes[0].Children);
            else
                findNonLeafs(_mainVm.SpaceVM.SpaceTypeTreeNodes);

            return nonLeafNodes.OrderBy(n => n.DisplayName);
        }

        private SpaceTypeTreeNodeViewModel FindParentNodeInTree(string nodeName)
        {
            var node = FindVmNodeByNameRecursive(_mainVm?.SpaceVM?.SpaceTypeTreeNodes, nodeName);
            return node?.Parent; // 直接返回父节点
        }

        private SpaceTypeTreeNodeViewModel FindVmNodeByNameRecursive(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes, string name)
        {
            if (nodes == null || string.IsNullOrWhiteSpace(name)) return null;
            foreach (var node in nodes)
            {
                if (node.DisplayName.Equals(name, StringComparison.OrdinalIgnoreCase)) return node;
                var foundInChildren = FindVmNodeByNameRecursive(node.Children, name);
                if (foundInChildren != null) return foundInChildren;
            }
            return null;
        }

        private string FindNodePathInTree(string nodeName)
        {
            var node = FindVmNodeByNameRecursive(_mainVm?.SpaceVM?.SpaceTypeTreeNodes, nodeName);
            return GetNodePath(node); // 使用 GetNodePath 辅助方法
        }

        private string GetNodePath(SpaceTypeTreeNodeViewModel node)
        {
            // 如果节点无效，或就是根节点，或没有父节点，或父节点是根，则返回 null（表示无法生成相对路径或就在根下）
            if (node == null || node.IsAllSpacesNode || node.Parent == null /*|| node.Parent.IsAllSpacesNode*/) // 考虑直接子节点的情况
                return null; // 返回 null 表示它在根下，或者无法确定路径

            // 如果父节点就是根节点 "All Spaces"，则路径就是节点自身的名字
            if (node.Parent.IsAllSpacesNode)
            {
                return node.DisplayName;
            }


            var pathSegments = new Stack<string>();
            var currentNode = node;
            // 向上遍历，直到父节点是代表所有空间的节点
            while (currentNode != null && currentNode.Parent != null && !currentNode.Parent.IsAllSpacesNode)
            {
                pathSegments.Push(currentNode.DisplayName);
                currentNode = currentNode.Parent;
                // 安全检查
                if (pathSegments.Count > 100)
                {
                    Debug.WriteLine($"GetNodePath 警告: 路径过深或可能存在循环，节点: '{node.DisplayName}'");
                    return null;
                }
            }
            // 将最后一个父节点（根下的直接子节点）也加入路径
            if (currentNode != null && currentNode.Parent != null && currentNode.Parent.IsAllSpacesNode)
            {
                pathSegments.Push(currentNode.DisplayName);
            }
            else
            {
                // 如果循环结束时 currentNode 或其父节点不满足预期，说明路径有问题
                Debug.WriteLine($"GetNodePath 警告: 未能正确追溯到根用户节点，节点: '{node.DisplayName}'");
                return null; // 无法构建有效路径
            }


            // Stack 后进先出，Join 结果是 Parent/Child 顺序
            return string.Join("/", pathSegments.Reverse()); // 使用 Reverse() 确保是 RootChild/GrandChild 顺序
        }
        /// <summary>
        /// 尝试使用多种常见编码读取 CSV 文件内容。
        /// </summary>
        /// <param name="filePath">CSV 文件路径。</param>
        /// <param name="detectedEncoding">输出参数，返回成功读取时使用的编码。</param>
        /// <returns>读取到的文件行数组，如果所有尝试都失败则抛出异常。</returns>
        /// <exception cref="IOException">当文件无法读取时抛出。</exception>
        /// <exception cref="Exception">当所有编码尝试都失败时抛出。</exception>
        private static string[] ReadCsvWithEncodingDetection(string filePath, out Encoding detectedEncoding)
        {
            detectedEncoding = null;
            string[] lines = null;
            string headerCheckString = "名称"; // 主要检查中文表头
            string alternativeHeaderCheck = "Name"; // 同时检查英文表头以增加兼容性

            var encodingsToTry = new List<Encoding>
    {
        Encoding.UTF8,             // 优先 UTF-8 (带或不带 BOM)
        Encoding.Default,          // 系统默认编码 (可能是 GBK/GB2312)
        new UTF8Encoding(false)    // 显式尝试 UTF-8 无 BOM
        // 可以考虑添加更多编码，如果需要的话
        // Encoding.GetEncoding("GBK"), // 需要 System.Text.Encoding.CodePages NuGet 包 (对于 .NET Core/5+) 或引用 (Framework)
        // Encoding.GetEncoding("GB2312"),
    };

            // 尝试显式添加 GBK (如果系统默认不是 GBK)
            try
            {
                Encoding gbkEncoding = Encoding.GetEncoding("GBK"); // .NET Framework 通常内置支持
                if (!encodingsToTry.Any(e => e.EncodingName == gbkEncoding.EncodingName))
                {
                    encodingsToTry.Insert(1, gbkEncoding); // 插入到 UTF-8 之后，Default 之前
                    Debug.WriteLine("Added GBK encoding to detection list.");
                }
            }
            catch (ArgumentException)
            { // .NET Framework 中如果不支持会是 ArgumentException
                Debug.WriteLine("GBK encoding not supported on this system or requires additional setup.");
            }
            catch (NotSupportedException)
            { // .NET Core/5+ 中是 NotSupportedException
                Debug.WriteLine("GBK encoding requires System.Text.Encoding.CodePages package.");
            }


            Debug.WriteLine($"--- Starting encoding detection for: {filePath} ---");
            foreach (var encoding in encodingsToTry)
            {
                try
                {
                    Debug.WriteLine($"Attempting to read with encoding: {encoding.EncodingName} (CodePage: {encoding.CodePage})");
                    lines = File.ReadAllLines(filePath, encoding);

                    if (lines.Length > 0)
                    {
                        string headerLine = lines[0];
                        Debug.WriteLine($"  Raw Header Line (len={headerLine.Length}): '{headerLine}'");

                        // 移除潜在的 BOM
                        if (headerLine.Length > 0 && headerLine[0] == '\uFEFF')
                        {
                            headerLine = headerLine.Substring(1);
                            Debug.WriteLine($"  Header Line after BOM removal: '{headerLine}'");
                        }

                        // 检查表头是否包含期望的关键词
                        if (!string.IsNullOrWhiteSpace(headerLine) &&
                            (headerLine.Contains(headerCheckString) || headerLine.Contains(alternativeHeaderCheck)))
                        {
                            Debug.WriteLine($"  SUCCESS: Header check passed with {encoding.EncodingName}.");
                            detectedEncoding = encoding;
                            // 如果原始第一行有 BOM，需要移除后再返回
                            if (lines[0].Length > 0 && lines[0][0] == '\uFEFF')
                            {
                                lines[0] = lines[0].Substring(1);
                            }
                            return lines;
                        }
                        else
                        {
                            Debug.WriteLine($"  WARNING: Header check failed with {encoding.EncodingName}. Header does not contain '{headerCheckString}' or '{alternativeHeaderCheck}'.");
                        }
                    }
                    else
                    {
                        Debug.WriteLine($"  WARNING: File is empty (0 lines) when read with {encoding.EncodingName}.");
                        // 如果文件为空，没有明确的“成功”编码，可以考虑直接返回空数组，或者继续尝试其他编码
                        // 这里我们继续尝试，因为空文件本身可能不是编码问题
                    }
                }
                catch (DecoderFallbackException decEx)
                {
                    Debug.WriteLine($"  ERROR: DecoderFallbackException with {encoding.EncodingName}: {decEx.Message}");
                }
                catch (IOException ioEx)
                {
                    Debug.WriteLine($"  ERROR: IOException with {encoding.EncodingName}: {ioEx.Message}");
                    detectedEncoding = null; // 明确设置失败
                    throw; // IO 错误通常是更严重的问题（如文件锁定），直接抛出让上层处理
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"  ERROR: General Exception with {encoding.EncodingName}: {ex.Message}");
                }
                // 重置 lines 为 null，以便下一次循环或最终检查
                lines = null;
            }
            Debug.WriteLine("--- Encoding detection finished: All attempts failed. ---");

            // 所有尝试都失败了
            detectedEncoding = null;
            throw new Exception($"无法使用尝试的编码格式 ({string.Join(", ", encodingsToTry.Select(e => e.EncodingName))}) 正确读取文件内容或识别有效的表头。请检查文件编码或内容。");
        }
        private void HvacItem_IsSelectedChanged(object sender, EventArgs e)
        {
            // Re-evaluate delete command's CanExecute status
            (DeleteSpaceTypeCommand as AsyncRelayCommand)?.NotifyCanExecuteChanged();
        }
        private string EscapeCsvFieldHelper(string field)
        {
            if (string.IsNullOrEmpty(field) || field == "N/A" || field == "错误" || field == "待确认参数" || field == "无") return "";
            if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r")) return $"\"{field.Replace("\"", "\"\"")}\"";
            return field;
        }
        private string FormatValueForCsvHelper(double? internalValue, ForgeTypeId displayUnitTypeId, string format, double zeroThreshold = 0.0)
        {
            if (!internalValue.HasValue) return "";
            try
            {
                double value = internalValue.Value;
                if (zeroThreshold > 0.0 && Math.Abs(value) < zeroThreshold) return 0.0.ToString(format, CultureInfo.InvariantCulture);
                if (displayUnitTypeId == null || string.IsNullOrEmpty(displayUnitTypeId.TypeId)) return EscapeCsvFieldHelper(value.ToString(format, CultureInfo.InvariantCulture));
                double displayValue = UnitUtils.ConvertFromInternalUnits(value, displayUnitTypeId);
                return EscapeCsvFieldHelper(displayValue.ToString(format, CultureInfo.InvariantCulture));
            }
            catch { return EscapeCsvFieldHelper("转换错误"); }
        }

        private string GetScheduleNameFromParamHelper(HVACLoadSpaceType spaceType, BuiltInParameter bip)
        {
            if (spaceType == null) return "无";
            Parameter param = spaceType.get_Parameter(bip);
            ElementId scheduleId = param?.AsElementId();
            if (spaceType.Document == null || scheduleId == null || scheduleId == ElementId.InvalidElementId) return "无";
            try { var schedule = spaceType.Document.GetElement(scheduleId); return schedule?.Name ?? "无效 ID"; }
            catch { return "读取错误"; }
        }

        private string FormatOutdoorAirStandardHelper(OutdoorAirFlowStandard standard)
        {
            switch (standard)
            {
                // *** 根据你找到的实际枚举成员名称进行映射 ***
                case OutdoorAirFlowStandard.ByPeopleAndByArea:
                    return "按人数和面积";
                case OutdoorAirFlowStandard.ByACH:
                    return "按换气次数";
                case OutdoorAirFlowStandard.MaxByPeople_ByArea:
                    return "最大值 (按人数, 按面积)";
                case OutdoorAirFlowStandard.MaxByACH_ByPeopleByArea:
                    return "最大值 (按换气次数, 按人数和面积)";
                case OutdoorAirFlowStandard.MaxByACH_ByArea_ByPeople:
                    return "最大值 (按换气次数, 按面积, 按人数)";
                default:
                    // 如果有其他未知的枚举值，直接显示其英文名称
                    Debug.WriteLine($"未知的 OutdoorAirFlowStandard 值: {standard}");
                    return standard.ToString();
            }
        }
        #endregion

        private string GetCsvValue(string[] values, Dictionary<string, int> headerIndexMap, string headerName, string defaultValue = "")
        {
            if (headerIndexMap.TryGetValue(headerName, out int index)) // 直接使用传入的中文或英文表头
            {
                if (index < values.Length && !string.IsNullOrWhiteSpace(values[index]))
                {
                    // 去除可能的引号
                    string val = values[index].Trim();
                    if (val.StartsWith("\"") && val.EndsWith("\""))
                    {
                        val = val.Substring(1, val.Length - 2).Replace("\"\"", "\""); // 处理转义引号
                    }
                    return val;
                }
            }
            return defaultValue;
        }

        private double? ParseCsvDouble(string value)
        {
            if (string.IsNullOrWhiteSpace(value)) return null;
            // 尝试去除可能的百分号
            value = value.Replace("%", "").Trim();
            if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out double result))
            {
                return result;
            }
            return null;
        }

        private bool ParseCsvBool(string value, bool defaultValue)
        {
            if (string.IsNullOrWhiteSpace(value)) return defaultValue;
            value = value.Trim().ToUpperInvariant(); // 转大写比较
            if (value == "TRUE" || value == "YES" || value == "1" || value == "是") return true;
            if (value == "FALSE" || value == "NO" || value == "0" || value == "否") return false;
            return defaultValue;
        }

        private Dictionary<string, int> GetHeaderIndexMap(string[] headers)
        {
            Dictionary<string, int> map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            for (int i = 0; i < headers.Length; i++)
            {
                string header = headers[i].Trim();
                // 去除可能的引号
                if (header.StartsWith("\"") && header.EndsWith("\""))
                {
                    header = header.Substring(1, header.Length - 2).Replace("\"\"", "\"");
                }
                if (!string.IsNullOrEmpty(header) && !map.ContainsKey(header))
                {
                    map.Add(header, i);
                }
            }
            return map;
        }
        // 设置属性的辅助方法

        private void SetScheduleParameterByName(HVACLoadSpaceType type, BuiltInParameter bip, string scheduleName)
        {
            if (!string.IsNullOrWhiteSpace(scheduleName))
            {
                // 查找具有该名称的明细表 ElementId
                ElementId scheduleId = FindScheduleIdByName(scheduleName);
                if (scheduleId != ElementId.InvalidElementId)
                {
                    Parameter param = type.get_Parameter(bip);
                    if (param != null && !param.IsReadOnly)
                    {
                        param.Set(scheduleId);
                    }
                }
                else
                {
                    Debug.WriteLine($"导入警告: 未找到名称为 '{scheduleName}' 的明细表，无法设置 {bip} 参数。");
                }
            }
        }

        // 设置新风标准的辅助方法
        private void SetOutdoorAirStandard(HVACLoadSpaceType type, string standardString)
        {
            if (type == null || string.IsNullOrWhiteSpace(standardString))
            {
                return; // 无效输入，不进行操作
            }

            OutdoorAirFlowStandard? resolvedStandard = null; // 使用可空类型来标记是否成功解析

            // 1. 优先尝试直接将字符串解析为枚举成员名 (忽略大小写)
            if (Enum.TryParse(standardString, true, out OutdoorAirFlowStandard parsedValue))
            {
                if (Enum.IsDefined(typeof(OutdoorAirFlowStandard), parsedValue))
                {
                    resolvedStandard = parsedValue;
                    Debug.WriteLine($"新风标准: 直接解析字符串 '{standardString}' 为枚举成员 {resolvedStandard.Value}");
                }
            }

            // 2. 如果直接解析失败，则尝试根据中文进行映射
            if (!resolvedStandard.HasValue)
            {
                Debug.WriteLine($"新风标准: 直接解析 '{standardString}' 失败，尝试中文映射...");
                switch (standardString)
                {
                    case "按人数和面积":
                        resolvedStandard = OutdoorAirFlowStandard.ByPeopleAndByArea;
                        break;
                    case "按换气次数":
                        resolvedStandard = OutdoorAirFlowStandard.ByACH;
                        break;
                    case "最大值 (按人数, 按面积)":
                        resolvedStandard = OutdoorAirFlowStandard.MaxByACH_ByPeopleByArea;
                        break;
                    case "最大值 (按换气次数, 按人数和面积)":
                        resolvedStandard = OutdoorAirFlowStandard.MaxByACH_ByArea_ByPeople;
                        break;
                    case "最大值 (按换气次数, 按面积, 按人数)":
                        resolvedStandard = OutdoorAirFlowStandard.MaxByACH_ByArea_ByPeople;
                        break;
                    default:
                        Debug.WriteLine($"导入警告: 无法识别或映射新风方法字符串 '{standardString}'。");
                        break; // 保持 resolvedStandard 为 null
                }
                if (resolvedStandard.HasValue)
                {
                    Debug.WriteLine($"新风标准: 通过中文映射 '{standardString}' 为枚举成员 {resolvedStandard.Value}");
                }
            }

            // 3. 如果成功解析出枚举值，则设置属性
            if (resolvedStandard.HasValue)
            {
                try
                {
                    // 检查当前值是否与要设置的值相同，避免不必要的事务更改
                    if (type.OutdoorAirFlowStandard != resolvedStandard.Value)
                    {
                        type.OutdoorAirFlowStandard = resolvedStandard.Value;
                        Debug.WriteLine($"成功设置类型 '{type.Name}' 的新风标准为: {resolvedStandard.Value}");
                    }
                    else
                    {
                        Debug.WriteLine($"类型 '{type.Name}' 的新风标准已经是 {resolvedStandard.Value}，无需设置。");
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"设置类型 '{type.Name}' 的 OutdoorAirFlowStandard 属性为 {resolvedStandard.Value} 时出错: {ex.Message}");
                }
            }
            // else: 如果解析失败，则不进行设置，并在前面已打印警告信息
        }

        // 查找明细表 ID
        private ElementId FindScheduleIdByName(string name)
        {
            if (string.IsNullOrWhiteSpace(name)) return ElementId.InvalidElementId;
            // 查找所有类型为 ViewSchedule 的元素
            var scheduleCollector = new FilteredElementCollector(_doc)
                .OfClass(typeof(ViewSchedule))
                .WhereElementIsNotElementType();
            // 找到名称匹配的第一个明细表
            var schedule = scheduleCollector.FirstOrDefault(s => s.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
            return schedule?.Id ?? ElementId.InvalidElementId;
        }

        // 导入数据的内部 DTO 类
        private class SpaceTypeDataIntegrated
        {
            public string Name { get; set; }
            public bool IsPlenum { get; set; }
            public double? AreaPerPerson_m2_person { get; set; }
            public double? LightingLoadDensity_W_m2 { get; set; }
            public double? PowerLoadDensity_W_m2 { get; set; }
            public double? SensibleHeatGainPerPerson_W { get; set; }
            public double? LatentHeatGainPerPerson_W { get; set; }
            public double? HeatingSetpoint_C { get; set; }
            public double? CoolingSetpoint_C { get; set; }
            public double? HumidificationSetpoint_Percent { get; set; }
            public double? DehumidificationSetpoint_Percent { get; set; }
            public double? InfiltrationAirflowPerArea_Lps_m2 { get; set; }
            public string OccupancyScheduleName { get; set; }
            public string LightingScheduleName { get; set; }
            public string PowerScheduleName { get; set; }
            public double? OutsideAirPerPerson_m3h { get; set; }
            public double? OutsideAirPerArea_Lps_m2 { get; set; }
            public double? AirChangesPerHour { get; set; }
            public string OutdoorAirFlowStandardString { get; set; }
        }


        private void BuildNodeLookupRecursive(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes, Dictionary<ElementId, SpaceTypeTreeNodeViewModel> lookup)
        {
            if (nodes == null || lookup == null) return;
            foreach (var node in nodes)
            {
                // Add node to lookup if it has a valid AssociatedSpaceTypeId
                if (node.AssociatedSpaceTypeId != null && node.AssociatedSpaceTypeId != ElementId.InvalidElementId)
                {
                    if (!lookup.ContainsKey(node.AssociatedSpaceTypeId)) // Avoid duplicates if tree structure allows
                    {
                        lookup.Add(node.AssociatedSpaceTypeId, node);
                    }
                    else
                    {
                        Debug.WriteLine($"BuildNodeLookupRecursive WARNING: Duplicate TypeId {node.AssociatedSpaceTypeId} found for node '{node.DisplayName}'.");
                    }
                }
                // Recurse into children
                if (node.Children != null)
                {
                    BuildNodeLookupRecursive(node.Children, lookup);
                }
            }
        }

        /// <summary>
        /// 递归在 SpaceTypeTreeNodeViewModel 树中按 TypeId 查找节点。
        /// (可以从 SpaceTabViewModel 复制或移至共享工具类)
        /// </summary>
        private SpaceTypeTreeNodeViewModel FindVmNodeByTypeIdRecursive(ObservableCollection<SpaceTypeTreeNodeViewModel> nodes, ElementId typeId)
        {
            if (nodes == null || typeId == null || typeId == ElementId.InvalidElementId) return null;
            foreach (var node in nodes)
            {
                // 直接比较 ID
                if (node.AssociatedSpaceTypeId == typeId) return node;
                // 递归查找子节点
                var foundInChildren = FindVmNodeByTypeIdRecursive(node.Children, typeId);
                if (foundInChildren != null) return foundInChildren;
            }
            return null; // 未找到
        }

    }
}
